#include <memory>
#include <mutex>

#include <crow/http_response.h>
#include <crow/mustache.h>

#include "common/api/client.h"

#include "common/config/config.h"

#include "common/helpers/template_helper.h"
#include "common/log/crow_logger.hpp"
#include "common/log/log.h"

#include "common/more/uuid.h"

#include "common/helpers/container_helper.hpp"
#include "common/helpers/crow_helper.h"

#include "ui/ui_logout_handler.h"
#include "ui/ui_root_handler.h"
#include "ui/ui_user_handler.h"
#include "ui/ui_users_handler.h"

#include "ui_controller.h"
#include "ui_session/ui_session.h"

namespace Ui
{

UiController::UiController(std::shared_ptr<Api::Client> datenaroGate)
    : m_crowApplication(new crow::SimpleApp)
    , m_datenaroGate(datenaroGate)
{
    static CrowUiLogger logger;
    crow::logger::setHandler(&logger);

    // Назначение базовой папки с шаблонами пользовательского интерфейса.
    Common::templateBaseDirectory() = crow::mustache::detail::get_template_base_directory_ref();

    m_loginPageTemplate = crow::mustache::template_t(
        Common::combineTemplates("login_page.html")
    );
    m_rootPageTemplate = crow::mustache::template_t(
        Common::combineTemplates("root_page.html")
    );
    m_userPageTemplate = crow::mustache::template_t(
        Common::combineTemplates(
            "user_page.html",
            {
                { "bottomBarLeft", "user_bottom_bar_left.html" },
            }
        )
    );
    m_newUserPageTemplate = crow::mustache::template_t(
        Common::combineTemplates(
            "new_user_page.html",
            {
                { "bottomBarLeft", "base/bottom_bar_left_save.html" },
            }
        )
    );
    m_usersPageTemplate = crow::mustache::template_t(
        Common::combineTemplates("users_page.html")
    );

    m_error404 = crow::mustache::template_t(
        Common::combineTemplates("errors/404_page.html")
    );

    RootHandler::setup((*m_crowApplication), this);
    LogoutHandler::setup((*m_crowApplication), this);
    UserHandler::setup((*m_crowApplication), this);
    UsersHandler::setup((*m_crowApplication), this);
}

UiController::~UiController()
{
}

void UiController::run()
{
    m_crowApplication->port(CONFIG.network.uiPort).multithreaded().run();
}

std::shared_ptr<Api::Client> UiController::datenaroGate()
{
    return m_datenaroGate;
}

const crow::mustache::template_t& UiController::loginPageTemplate() const
{
    return m_loginPageTemplate;
}

const crow::mustache::template_t& UiController::rootPageTemplate() const
{
    return m_rootPageTemplate;
}

const crow::mustache::template_t& UiController::userPageTemplate() const
{
    return m_userPageTemplate;
}

const crow::mustache::template_t& UiController::usersPageTemplate() const
{
    return m_usersPageTemplate;
}

const crow::mustache::template_t& UiController::newUserPageTemplate() const
{
    return m_newUserPageTemplate;
}

const crow::mustache::template_t& UiController::error404() const
{
    return m_error404;
}

UiSessionPtr UiController::addSession(const Uuids::Uuid& sessionUuid, unsigned userId)
{
    auto result = std::make_shared<UiSession>(sessionUuid, userId, m_datenaroGate);
    std::unique_lock locker(m_sessionsUserIdsLock);
    m_sessions[sessionUuid] = result;
    return result;
}

void UiController::delSession(const Uuids::Uuid& sessionUuid)
{
    std::unique_lock locker(m_sessionsUserIdsLock);
    m_sessions.erase(sessionUuid);
}

UiSessionPtr UiController::session(const Uuids::Uuid& sessionUuid) const
{
    if (Uuids::nilUuid() == sessionUuid)
        return nullptr;

    std::unique_lock locker(m_sessionsUserIdsLock);
    return Common::getDictValue(m_sessions, sessionUuid, nullptr);
}

} // namespace Ui
